package schema

import (
	"fmt"
	"gitee.com/zhendliu/sorm/parser"
	"gitee.com/zhendliu/sorm/spliter"
	"go/ast"
	"reflect"
)

// Field represents a column of database
type Field struct {
	Name   string
	Tag    string
	Column string
}

// Schema represents a table of database
type Schema struct {
	Model        interface{}
	Name         string
	Fields       []*Field // 结构中的field
	FieldNames   []string // sql 中的field
	fieldMap     map[string]*Field
	SubTableFile string
	Auto         string
}

// GetField returns field by name
func (schema *Schema) GetField(name string) *Field {
	return schema.fieldMap[name]
}

func (schema *Schema) RecordValuesCustom(val reflect.Value, dest interface{}, selectField []string, omitField []string) []interface{} {
	//	destValue := reflect.Indirect(reflect.ValueOf(dest))

	fieldValues := make([]interface{}, 0, len(schema.Fields))

	selectFieldMap := make(map[string]bool)
	for _, sf := range selectField {
		selectFieldMap[sf] = true
	}

	omitFieldMap := make(map[string]bool)
	for _, of := range omitField {
		omitFieldMap[of] = true
	}

	for _, field := range schema.Fields {
		if len(selectField) == 0 || selectFieldMap[field.Column] {
			if len(omitField) == 0 || !omitFieldMap[field.Column] {
				fieldValue := val.FieldByName(field.Name).Interface()

				fieldValues = append(fieldValues, fieldValue)
			}
		}
	}

	return fieldValues
}

type ITableName interface {
	TableName() string
}

// Parse a struct to a Schema instance
func Parse(dbSchema string, split map[string]spliter.Split, dest interface{}) *Schema {
	modelType := reflect.Indirect(reflect.ValueOf(dest)).Type()
	var (
		tableName    string
		subTableFile string
		err          error
	)

	tableName, _, err = parser.GetTableNameAndSubTableField(dest)
	if err != nil {
		// use default table info
		t, ok := dest.(ITableName)
		if !ok {
			tableName = modelType.Name()
		} else {
			tableName = t.TableName()
		}
	}

	if sp, ok := split[tableName]; ok {
		subTableFile = sp.TableSegName
	}

	if dbSchema != "" {
		tableName = fmt.Sprintf("%s.%s", dbSchema, tableName)
	}

	schema := &Schema{
		Model:        dest,
		Name:         tableName,
		fieldMap:     make(map[string]*Field),
		SubTableFile: subTableFile,
	}
	for i := 0; i < modelType.NumField(); i++ {
		p := modelType.Field(i)
		if !p.Anonymous && ast.IsExported(p.Name) {

			// 获取tag中指定的column名称
			column := getColumnTag(p)

			field := &Field{
				Name: p.Name,
			}

			/*field := &Field{
				Name: column,
			}*/
			var haveAuto bool

			if _, ok := p.Tag.Lookup("auto"); ok {
				haveAuto = true
			}

			if v, ok := p.Tag.Lookup("column"); ok {
				field.Column = v
				if haveAuto {
					schema.Auto = v
				}
			}

			schema.Fields = append(schema.Fields, field)
			schema.FieldNames = append(schema.FieldNames, column)
			schema.fieldMap[p.Name] = field
		}
	}

	return schema
}

func ParseTable(table string) *Schema {
	schema := &Schema{
		Name:     table,
		fieldMap: make(map[string]*Field),
	}
	return schema
}

func getColumnTag(field reflect.StructField) string {
	return field.Tag.Get("column")
}
